/*
 * 上海远境文化传媒有限公司 版权所有
 */
package ext.play;

import helper.GlobalConfig;
import helper.RedisLock;
import org.apache.commons.lang.StringUtils;
import play.Logger;
import play.jobs.Job;
import play.modules.redis.Redis;
import play.modules.redis.RedisConnectionManager;
import util.play.TimeTracker;

import java.util.Date;
import java.util.Set;

/**
 * 通过Redis，保证同一时间(10秒内)只有一个Jobs能运行
 */
public abstract class JobWithLock<V> extends Job<V> {

    private final static String REDIS_KEY_LAST_BEGIN_RUN_AT = "hys.run-jobs.";

    private final static boolean useRedis = StringUtils.isNotBlank(GlobalConfig.REDIS_URL);

    /**
     * 如果另一个Job实例在指定毫秒中运行过，此job不再执行.
     * 默认10000毫秒，即10秒
     *
     * @return 毫秒.
     */
    private long getDisableWhenOtherJobsRunInMillis() {
        return 10000L;
    }

    private String getLastBeginRunAtRedisKey() {
        return REDIS_KEY_LAST_BEGIN_RUN_AT + getClass().getName();
    }

    /**
     * 清除最后运行记录，单元测试前调用一下，以避免不行第二次执行job的问题.
     */
    public static void cleanLastBeginRunAtForTest() {
        if (useRedis) {
            Set<String> keys = Redis.keys(REDIS_KEY_LAST_BEGIN_RUN_AT + "*");
            for (String key : keys) {
                Redis.del(new String[]{key});
            }
        }
    }

    /**
     * doJob中记录运行历史，发到MQ中保存
     * 并把doJob转到doJobWithHistory方法中
     */
    public final void doJob() throws Exception {
        TimeTracker.startTrack();
        long beginAt = System.currentTimeMillis();

        try {
            if (useRedis) {
                // 为避免多个jvm之间重复 10秒内再次运行的job不被执行
                if (Redis.exists(getLastBeginRunAtRedisKey())) {
                    long lastBeginAt = Long.parseLong(Redis.get(getLastBeginRunAtRedisKey()));
                    if (Math.abs(lastBeginAt - beginAt) < getDisableWhenOtherJobsRunInMillis()) {
                        // 在指定时间（10秒）内被运行过
                        Logger.info("Job:" + this.getClass().getName() + "被另一进程在" + (new Date(lastBeginAt)) + "运行过，不再运行.");
                        return;
                    }
                }
                Redis.set(getLastBeginRunAtRedisKey(), String.valueOf(beginAt));
            }
            doJobWithLock();
        } catch (Exception e) {
            Logger.warn(e, "LOG821762: JobWithLock异常");
            throw e;
        } finally {
            if (useRedis) {
                RedisLock.unlockAll();
                RedisConnectionManager.closeConnection();
            }
            TimeTracker.printTimeOutTrack();
        }

    }


    public abstract void doJobWithLock();
}
